﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace V32VM
{
    class Registers
    {
        private byte _a, _b, _c, _d;
        private uint _s;
        public byte e, f, g;
        public uint t, u, v, w, x, y, z;
        public uint ip, sp;
        public byte fl;

        public byte a
        {
            get { return _a; }
            set { _a = value; _s = (_s & 0xFFFFFF00) | (uint)(_a >> 0); }
        }
        public byte b
        {
            get { return _b; }
            set { _b = value; _s = (_s & 0xFFFF00FF) | (uint)(_b >> 8); }
        }
        public byte c
        {
            get { return _c; }
            set { _c = value; _s = (_s & 0xFF000000) | (uint)(_c >> 16); }
        }
        public byte d
        {
            get { return _d; }
            set { _d = value; _s = (_s & 0x00FFFFFF) | (uint)(_d >> 24); }
        }

        public uint s
        {
            get { return _s; }
            set { _s = value; updateABCD(); }
        }

        public Registers()
        {
        }

        public Registers(string file)
        {
            string[] lines = File.ReadAllLines(file);
            lines = (from line in lines
                     where line.Trim() != ""
                     select line.Trim()).ToArray();
            foreach (string line in lines)
            {
                if (!line.Contains(' '))
                    throw new ArgumentException("file");
                int i = line.IndexOf(' ');
                string reg = line.Remove(i);
                string strVal = line.Substring(i + 1).TrimStart();
                uint val = uint.Parse(strVal);

                switch (reg.ToLower())
                {
                    case "a": _a = (byte)val; break;
                    case "b": _b = (byte)val; break;
                    case "c": _c = (byte)val; break;
                    case "d": _d = (byte)val; break;
                    case "e": e = (byte)val; break;
                    case "f": f = (byte)val; break;
                    case "g": g = (byte)val; break;
                    case "t": t = (byte)val; break;
                    case "u": u = (byte)val; break;
                    case "v": v = (byte)val; break;
                    case "w": w = (byte)val; break;
                    case "x": x = (byte)val; break;
                    case "y": y = (byte)val; break;
                    case "z": z = (byte)val; break;
                    case "s": _s = (byte)val; break;
                    case "ip": ip = (byte)val; break;
                    case "sp": sp = (byte)val; break;
                    case "fl": fl = (byte)val; break;
                    default: throw new Exception("No such register '" + reg + "'.");
                }
            }
        }

        void updateABCD()
        {
            _a = (byte)(_s >> 0);
            _b = (byte)(_s >> 8);
            _c = (byte)(_s >> 16);
            _d = (byte)(_s >> 24);
        }

        public void saveToFile(string file)
        {
            File.WriteAllLines(file, new string[] {
                "s " + _s,
                "e " + e,
                "f " + f,
                "g " + g,
                "t " + t,
                "u " + u,
                "v " + v,
                "w " + w,
                "x " + x,
                "y " + y,
                "z " + z,
                "ip " + ip,
                "sp " + sp,
                "fl " + fl,
            });
        }

        public void pushToStack(Memory memory, uint address)
        {
            memory.words[ip, address += 4] = s;
            memory[ip, address++] = e;
            memory[ip, address++] = f;
            memory[ip, address++] = g;
            memory.words[ip, preinc(ref address, 4)] = t;
            memory.words[ip, preinc(ref address, 4)] = u;
            memory.words[ip, preinc(ref address, 4)] = v;
            memory.words[ip, preinc(ref address, 4)] = w;
            memory.words[ip, preinc(ref address, 4)] = x;
            memory.words[ip, preinc(ref address, 4)] = y;
            memory.words[ip, preinc(ref address, 4)] = z;
            memory.words[ip, preinc(ref address, 4)] = ip;
            memory.words[ip, preinc(ref address, 4)] = sp;
            memory[ip, address++] = fl;
        }
        public static Registers popFromStack(Memory memory, uint ip, uint address)
        {
            Registers regs = new Registers();
            regs.fl = memory[ip, --address];
            regs.sp = memory.words[ip, predec(ref address, 4)];
            regs.ip = memory.words[ip, predec(ref address, 4)];
            regs.z = memory.words[ip, predec(ref address, 4)];
            regs.y = memory.words[ip, predec(ref address, 4)];
            regs.x = memory.words[ip, predec(ref address, 4)];
            regs.w = memory.words[ip, predec(ref address, 4)];
            regs.v = memory.words[ip, predec(ref address, 4)];
            regs.u = memory.words[ip, predec(ref address, 4)];
            regs.t = memory.words[ip, predec(ref address, 4)];
            regs.g = memory[ip, --address];
            regs.f = memory[ip, --address];
            regs.e = memory[ip, --address];
            regs.s = memory.words[ip, predec(ref address, 4)];
            return regs;
        }

        static uint preinc(ref uint i, int c)
        {
            return i += (uint)c;
            //i = (uint)(i + c);
            //return i;
        }
        static uint predec(ref uint i, int c)
        {
            i = (uint)(i - c);
            return i;
        }

        public static uint FromString(string str)
        {
            switch (str.ToLower())
            {
                case "a": return 0;
                case "b": return 1;
                case "c": return 2;
                case "d": return 3;
                case "e": return 4;
                case "f": return 5;
                case "g": return 6;
                case "t": return 7;
                case "u": return 8;
                case "v": return 9;
                case "w": return 10;
                case "x": return 11;
                case "y": return 12;
                case "z": return 13;
                case "s": return 14;
                case "ip": return 15;
                case "sp": return 16;
                case "fl": return 17;
                default: throw new Exception("No such register '" + str + "'.");
            }
        }
        public static string ToString(uint reg)
        {
            switch (reg)
            {
                case 0: return "a";
                case 1: return "b";
                case 2: return "c";
                case 3: return "d";
                case 4: return "e";
                case 5: return "f";
                case 6: return "g";
                case 7: return "t";
                case 8: return "u";
                case 9: return "v";
                case 10: return "w";
                case 11: return "x";
                case 12: return "y";
                case 13: return "z";
                case 14: return "s";
                case 15: return "ip";
                case 16: return "sp";
                case 17: return "fl";
                default: throw new Exception("No register with index '" + reg + "'.");
            }
        }
    }
}
